/******************************************************************************
* Filename:    Chat Server.cpp
* Auther:	   Cheng Zhuo
*			   XPEC Entertainment Inc., Shanghai.
*
* Description: The server of chat application
*******************************************************************************
* (c) 2010 XPEC Entertainment Inc.,Shanghai. All rights reserved.
******************************************************************************/

#include "MessageIdentifiers.h"
#include "RakNetworkFactory.h"
#include "RakPeerInterface.h"
#include "RakNetStatistics.h"
#include "RakNetTypes.h"
#include "BitStream.h"
#include "RakSleep.h"
#include "ChatOrder.h"
#include "ChatServerPlugin.h"
#include <assert.h>
#include <cstdio>
#include <cstring>
#include <stdlib.h>

#ifdef _WIN32
#include "Kbhit.h"
#endif

#include <stdio.h>
#include <string.h>

#if defined(_CONSOLE_2)
#include "Console2SampleIncludes.h"
#endif



#ifdef _CONSOLE_2
_CONSOLE_2_SetSystemProcessParams
#endif


// Declaration of functions
//unsigned char GetPacketIdentifier( Packet *p );
//char* subString( char* message, int begin, int end );
//void initMember( SystemAddressMember& sam );
//bool existFriend( int mem1, int mem2 );

/******************************************************************************
 * Main function of this application                               
 * 
 * @return 1 on finished
 *****************************************************************************/

int main( void )
{
	//
	// Pointers to the interfaces of our server and client.
	// Note we can easily have both in the same program
	RakPeerInterface *server=RakNetworkFactory::GetRakPeerInterface();
	RakNetStatistics *rss;
	//server->InitializeSecurity(0,0,0,0);
	int i = server->GetNumberOfAddresses();
	server->SetIncomingPassword("Rumpelstiltskin", (int)strlen("Rumpelstiltskin"));

	ChatApp::ChatServerPlugin chatserver;
	// Holds packets
	Packet* p;

	// GetPacketIdentifier returns this
	unsigned char packetIdentifier;

	// Record the first client that connects to us so we can pass it to the ping function
	SystemAddress clientID=UNASSIGNED_SYSTEM_ADDRESS;

	// Holds user data
	char portstring[30];

	// Output information
	printf("This is a sample implementation of a text based chat server.\n");
	printf("Connect to the project 'Chat Example Client'.\n");
	printf("Difficulty: Beginner\n\n");

	// A server
//	puts("Enter the server port to listen on");
//	gets(portstring);
//	if (portstring[0]==0)
	strcpy(portstring, "20121");
	
	puts("Starting server.");
	// Starting the server is very simple.  2 players allowed.
	// 0 means we don't care about a connectionValidationInteger, and false
	// for low priority threads
	SocketDescriptor socketDescriptor(atoi(portstring),0);
	bool begin = server->Startup(32, 30, &socketDescriptor, 1 );
	server->SetMaximumIncomingConnections(MAX_INCOMING_CONNECTIONS);
	server->AttachPlugin(&chatserver);

	// Check if the sever have been started
	if ( begin )
	{
		puts("Server started, waiting for connections.");
	} // End if
	else
	{ 
		puts("Server failed to start.  Terminating.");
		exit(1);
	} // End else

	// Set for Occasional ping
	server->SetOccasionalPing(true);

	// Output system address and order information
	printf("My IP is %s\n", server->GetLocalIP(0));
	printf("My GUID is %s\n", server->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS).ToString());
	puts("'quit' to quit. 'stat' to show stats. 'ping' to ping.\n'ban' to ban an IP from connecting.\n'kick' to kick the first connected player.\nType to talk.");

	// String of input message
	char message[2048];

	// Loop for input
	while ( 1 )
	{

	// This sleep keeps RakNet responsive
	RakSleep(30);

#ifdef _WIN32

	    // Check the status of keyboard
		if ( kbhit() )
		{
			// Notice what is not here: something to keep our network running.  It's
			// fine to block on gets or anything we want
			// Because the network engine was painstakingly written using threads.
			gets(message);

			// Quit order
			if (strcmp(message, "quit")==0)
			{
				puts("Quitting.");
				break;
			} // End if

			// Stat order
			if (strcmp(message, "stat")==0)
			{
				rss=server->GetStatistics(server->GetSystemAddressFromIndex(0));
				StatisticsToString(rss, message, 2);

				// Out put status message
				printf("%s", message);
				printf("Ping %i\n", server->GetAveragePing(server->GetSystemAddressFromIndex(0)));
		
				continue;
			} // End if

			// Ping order
			if (strcmp(message, "ping")==0)
			{
				server->Ping(clientID);

				continue;
			} // End if

			// Kick order
			if (strcmp(message, "kick")==0)
			{
				server->CloseConnection(clientID, true, 0);

				continue;
			} // End if


			// Ban order
			if (strcmp(message, "ban")==0)
			{
				printf("Enter IP to ban.  You can use * as a wildcard\n");

				// Get IP address which to ban
				gets(message);
				server->AddToBanList(message);

				printf("IP %s added to ban list.\n", message);

				continue;
			} // End if


			// Message now holds what we want to broadcast
			char message2[2048];
			// Append Server: to the message so clients know that it ORIGINATED from the server
			// All messages to all clients come from the server either directly or by being
			// relayed from other clients
			message2[0]=0;
			strcpy(message2, "Server: ");
			strcat(message2, message);
		
			// message2 is the data to send
			// strlen(message2)+1 is to send the null terminator
			// HIGH_PRIORITY doesn't actually matter here because we don't use any other priority
			// RELIABLE_ORDERED means make sure the message arrives in the right order
			// We arbitrarily pick 0 for the ordering stream
			// UNASSIGNED_SYSTEM_ADDRESS means don't exclude anyone from the broadcast
			// true means broadcast the message to everyone connected
			server->Send(message2, 
				        (const int) strlen(message2)+1, 
						HIGH_PRIORITY, 
						RELIABLE_ORDERED, 
						0, 
						UNASSIGNED_SYSTEM_ADDRESS, 
						true);
		} // End if
#endif

		// Get a packet from either the server or the client
		p = server->Receive();
		
		if (p==0)
			continue; // Didn't get any packets

		// We got a packet, get the identifier with our handy function
		packetIdentifier = chatserver.GetPacketId(p);

		// Check if this is a network message packet
		switch ( packetIdentifier )
		{
			case ID_DISCONNECTION_NOTIFICATION:
				{
				    // Connection lost normally
					printf("ID_DISCONNECTION_NOTIFICATION\n");
				} // End case

				break;
		
			case ID_NEW_INCOMING_CONNECTION:
				{
					// Somebody connected.  We have their IP now
					printf("ID_NEW_INCOMING_CONNECTION from %s with GUID %s.\n",
						   p->systemAddress.ToString(), 
						   p->guid.ToString());
					clientID=p->systemAddress; // Record the player ID of the client
				} // End case

				break;

			case ID_MODIFIED_PACKET:
				{
					// Cheater!
					printf("ID_MODIFIED_PACKET\n");
				} // End case
				
				break;

			case ID_CONNECTION_LOST:
				{
					// Couldn't deliver a reliable packet
					// terminated
					printf("ID_CONNECTION_LOST\n");

					// Initialization of the lost position
				} // End case
				
				break;

		} // End switch

		// We're done with the packet
		server->DeallocatePacket(p);
	}

	server->Shutdown(300);
	// We're done with the network
	RakNetworkFactory::DestroyRakPeerInterface(server);

	return 0;
} // End of main

///******************************************************************************
// * Function for getting the identifier of packet     
// * If the first byte is ID_TIMESTAMP, then we want the 5th byte
// * Otherwise we want the 1st byte
// *
// * @param p pointer point to a packet
// * 
// * @return The unsigned char signed the identifier
// *****************************************************************************/
//unsigned char GetPacketIdentifier( Packet *p )
//{
//	if ( p==0 )
//	{
//		return 255;
//	} // End if
//
//	if ( (unsigned char)p->data[0] == ID_TIMESTAMP )
//	{
//		RakAssert(p->length > sizeof(unsigned char) + sizeof(unsigned long));
//		return (unsigned char) p->data[sizeof(unsigned char) + sizeof(unsigned long)];
//	} // End if
//	else
//	{
//	    return (unsigned char) p->data[0];
//	} // End else
//} // End of GetPacketIdentifier
//
///******************************************************************************
// * Function for initializing the incoming member of the chatter     
// * If the connection is open, then close
// * Initialize the system address
// * Initialize the friend list to all -1
// *
// * @param sam instance of SystemAddressMember
// *****************************************************************************/
//void initMember( SystemAddressMember& sam )
//{
//	if( sam.isConnected )
//	{
//		sam.isConnected = false;
//	}
//
//	sam.sa.SetBinaryAddress("0.0.0.0");
//
//	for( int i = 0; i < MAX_FRIEND_NUM; ++i )
//	{
//		sam.friendId[i] = -1;
//	}
//} // End of initMember
//
///******************************************************************************
// * Function for checking the friend status     
// * Check both the tow client
// *
// * @param mem1 system index of client 1
// * @param mem2 system index of client 2
// *
// * @return True on exist
// *****************************************************************************/
//bool existFriend( int mem1, int mem2 )
//{
//	bool existFriend = false;
//	for( int i = 0; i < MAX_FRIEND_NUM; ++i )
//	{
//		if( m_sam[mem1].friendId[i] == mem2 )
//		{
//			existFriend = true;
//			break;
//		}
//		if( i == MAX_FRIEND_NUM - 1 )
//		{
//			existFriend = false;
//		}
//	}
//
//	for( int i = 0; i < MAX_FRIEND_NUM; ++i )
//	{
//		if( m_sam[mem2].friendId[i] == mem1 )
//		{
//			existFriend = true;
//			break;
//		}
//		if( i == MAX_FRIEND_NUM - 1 ) 
//		{
//			existFriend = false;
//		}
//	}
//
//	return existFriend;
//} // End of existFriend